// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	1/6/2015 12:40:00 AM				
// -----------------------------

#include "VolumeEnvelope.h"

VolumeEnvelope::VolumeEnvelope()
: AttackTime(0.0f)
, AttackValue(0.0f)
, DecayTime(0.0f)
, SustainValue(0.0f)
, ReleaseTime(0.0f)
{
}

VolumeEnvelope::~VolumeEnvelope()
{
}

SoundSample VolumeEnvelope::Apply(const SoundSample& input) const
{
	Float duration = input.Duration();

	SoundSample sample = {};
	sample.Channels = input.Channels;
	sample.SampleRate = input.SampleRate;
	sample.Count = input.Count;
	sample.Samples = new Float[sample.Count];
	memset(sample.Samples, 0, sample.Count * sizeof(Float));

	Float absAttackTime = duration * AttackTime;
	Float absDecayTime = duration * DecayTime;
	Float absReleaseTime = duration * ReleaseTime;

	// Calculate the amount of samples needed
	UInt32 attackSamples = (UInt32)((input.SampleRate * absAttackTime) * input.Channels);
	if (attackSamples % 2 == 1)
		++attackSamples;
	UInt32 decaySamples = (UInt32)((input.SampleRate * absDecayTime) * input.Channels);
	if (decaySamples % 2 == 1)
		++decaySamples;
	UInt32 releaseSamples = (UInt32)((input.SampleRate * absReleaseTime) * input.Channels);
	if (releaseSamples % 2 == 1)
		++releaseSamples;
	UInt32 sustainSamples = input.Count - (attackSamples + decaySamples + releaseSamples);

	UInt32 sampleOffset = 0;

	// Attack
	for (UInt32 i = 0; i < attackSamples / input.Channels; ++i)
	{
		Float t = ((Float)i / input.SampleRate) / AttackTime;
		Float v = Math::Lerp(0.0f, AttackValue, t);
		
		UInt32 idx = sampleOffset + (i * 2);
		for (UInt32 c = 0; c < input.Channels; ++c)
			sample.Samples[idx + c] = input.Samples[idx + c] * Math::Clamp(v);
	}
	sampleOffset += attackSamples;

	// Decay
	for (UInt32 i = 0; i < decaySamples / input.Channels; ++i)
	{
		Float t = ((Float)i / input.SampleRate) / DecayTime;
		Float v = Math::Lerp(AttackValue, SustainValue, t);

		UInt32 idx = sampleOffset + (i * 2);
		for (UInt32 c = 0; c < input.Channels; ++c)
			sample.Samples[idx + c] = input.Samples[idx + c] * Math::Clamp(v);
	}
	sampleOffset += decaySamples;

	// Sustain
	for (UInt32 i = 0; i < sustainSamples / input.Channels; ++i)
	{
		UInt32 idx = sampleOffset + (i * 2);
		for (UInt32 c = 0; c < input.Channels; ++c)
			sample.Samples[idx + c] = input.Samples[idx + c] * Math::Clamp(SustainValue);
	}
	sampleOffset += sustainSamples;

	// Release
	for (UInt32 i = 0; i < releaseSamples / input.Channels; ++i)
	{
		Float t = ((Float)i / input.SampleRate) / ReleaseTime;
		Float v = Math::Lerp(SustainValue, 0.0f, t);

		UInt32 idx = sampleOffset + (i * 2);
		for (UInt32 c = 0; c < input.Channels; ++c)
			sample.Samples[idx + c] = input.Samples[idx + c] * Math::Clamp(v);
	}

	return sample;
}